﻿using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;

namespace O1.Kernel.Storage.Transactions
{
    internal static class TxExtensions
    {
        public const ulong MinCommittedId = ~TransactionStatusMask;

        public const ulong MaxUncommittedId = MinCommittedId - 1;

        public const ulong TransactionIdMaskByte = 0x7F;

        public const ulong TransactionStatusMask = 0x7FFFFFFFFFFFFFFF;

        public const ulong TransactionIdMask = 0x8000000000000000;

        private const byte TransactionStatusMaskByte = 0x7F;

        private const int DmlStatusShift = 63;

        private const byte DmlStatusShiftByte = 7;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static TxStatus Status(this ulong transactionId)
        {
            return (TxStatus)(transactionId >> DmlStatusShift);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static ulong EmbedStatus(this ulong transactionId, TxStatus status)
        {
            return (((ulong)status) << DmlStatusShift) | (transactionId & TransactionStatusMask);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static ulong MaskCommitted(this ulong transactionId)
        {
            return transactionId | TxExtensions.TransactionIdMask;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static ulong UnmaskCommitted(this ulong transactionId)
        {
            return transactionId & TxExtensions.TransactionStatusMask;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static TxStatus Status(this byte[] buffer, int offset = 0)
        {
            Contract.Requires(offset > -1);
            Contract.Requires(buffer.Length - offset > 7);

            // Little endian:
            return (TxStatus)(buffer[offset + 7] >> DmlStatusShiftByte);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void EmbedStatus(this byte[] buffer, TxStatus status, int offset = 0)
        {
            Contract.Requires(offset > -1);
            Contract.Requires(buffer.Length - offset > 7);

            // Little endian:
            var value = (byte)status << DmlStatusShiftByte;
            buffer[offset + 7] = (byte)(value | (byte)(buffer[offset + 7] & TransactionStatusMaskByte));
        }
    }
}
